home *** CD-ROM | disk | FTP | other *** search
/ Delphi Developer's Kit 1996 / Delphi Developer's Kit 1996.iso / power / wfc007.000 / src / csvcmgr.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-22  |  17.7 KB  |  664 lines

  1. #include <wfc.h>
  2. #pragma hdrstop
  3.  
  4. /*
  5. ** Author: Samuel R. Blackburn
  6. ** CI$: 76300,326
  7. ** Internet: sammy@sed.csc.com
  8. **
  9. ** You can use it any way you like.
  10. */
  11.  
  12. #if defined( _DEBUG )
  13. #undef THIS_FILE
  14. static char BASED_CODE THIS_FILE[] = __FILE__;
  15. #endif
  16.  
  17. CServiceControlManager::CServiceControlManager()
  18. {
  19.    m_Initialize();
  20. }
  21.  
  22. CServiceControlManager::~CServiceControlManager()
  23. {
  24.    Close();
  25.  
  26.    if ( m_Buffer != NULL )
  27.    {
  28.       delete [] m_Buffer;
  29.       m_Buffer     = NULL;
  30.       m_BufferSize = 0;
  31.    }
  32. }
  33.  
  34. void CServiceControlManager::Close( void )
  35. {
  36.    if ( m_ManagerHandle != NULL )
  37.    {
  38.       if ( ::CloseServiceHandle( m_ManagerHandle ) == FALSE )
  39.       {
  40.          m_ErrorCode = ::GetLastError();
  41.       }
  42.  
  43.       m_ManagerHandle = NULL;
  44.    }
  45. }
  46.  
  47. BOOL CServiceControlManager::Continue( LPCTSTR service_name )
  48. {
  49.    ASSERT( service_name != NULL );
  50.  
  51.    if ( m_ManagerHandle == NULL )
  52.    {
  53.       m_ErrorCode = ERROR_INVALID_HANDLE;
  54.       return( FALSE );
  55.    }
  56.  
  57.    if ( service_name == NULL )
  58.    {
  59.       m_ErrorCode = ERROR_INVALID_HANDLE;
  60.       return( FALSE );
  61.    }
  62.  
  63.    SC_HANDLE service_handle = ::OpenService( m_ManagerHandle, service_name, SERVICE_PAUSE_CONTINUE );
  64.  
  65.    if ( service_handle == (SC_HANDLE) NULL )
  66.    {
  67.       m_ErrorCode = ::GetLastError();
  68.       return( FALSE );
  69.    }
  70.  
  71.    SERVICE_STATUS service_status;
  72.  
  73.    ::ZeroMemory( &service_status, sizeof( service_status ) );
  74.  
  75.    BOOL return_value = ::ControlService( service_handle, SERVICE_CONTROL_CONTINUE, &service_status );
  76.  
  77.    if ( return_value != TRUE )
  78.    {
  79.       m_ErrorCode = ::GetLastError();
  80.    }
  81.  
  82.    ::CloseServiceHandle( service_handle );
  83.  
  84.    return( return_value );
  85. }
  86.  
  87. BOOL CServiceControlManager::EnumerateStatus( DWORD state, DWORD type )
  88. {
  89.    /*
  90.    ** For GetNext() calls
  91.    */
  92.  
  93.    if ( m_ManagerHandle == NULL )
  94.    {
  95.       m_ErrorCode = ERROR_INVALID_HANDLE;
  96.       return( FALSE );
  97.    }
  98.  
  99.    if ( m_Buffer == NULL )
  100.    {
  101.       m_BufferSize = 128 * sizeof( ENUM_SERVICE_STATUS );
  102.  
  103.       m_Buffer = (ENUM_SERVICE_STATUS *) new BYTE[ m_BufferSize ];
  104.  
  105.       if ( m_Buffer == NULL )
  106.       {
  107.          m_BufferSize = 0;
  108.          m_ErrorCode = ::GetLastError();
  109.          return( FALSE );
  110.       }
  111.    }
  112.  
  113.    DWORD number_of_bytes_needed      = 0;
  114.    DWORD number_of_services_returned = 0;
  115.    DWORD resume_handle               = 0;
  116.  
  117.    if ( ::EnumServicesStatus( m_ManagerHandle,
  118.                               type,
  119.                               state,
  120.                               m_Buffer,
  121.                               m_BufferSize,
  122.                               &number_of_bytes_needed,
  123.                               &number_of_services_returned,
  124.                               &resume_handle ) == TRUE )
  125.    {
  126.       m_CurrentEntryNumber = 0;
  127.       m_NumberOfEntries    = number_of_services_returned;
  128.  
  129.       return( TRUE );
  130.    }
  131.  
  132.    m_ErrorCode = ::GetLastError();
  133.  
  134.    if ( m_ErrorCode == ERROR_MORE_DATA )
  135.    {
  136.       delete [] m_Buffer;
  137.  
  138.       m_Buffer = (ENUM_SERVICE_STATUS *) new BYTE[ number_of_bytes_needed ];
  139.  
  140.       if ( m_Buffer != NULL )
  141.       {
  142.          m_BufferSize = number_of_bytes_needed;
  143.       }
  144.       else
  145.       {
  146.          m_ErrorCode = ::GetLastError();
  147.          return( FALSE );
  148.       }
  149.  
  150.       number_of_bytes_needed      = 0;
  151.       number_of_services_returned = 0;
  152.       resume_handle               = 0;
  153.  
  154.       if ( ::EnumServicesStatus( m_ManagerHandle,
  155.                                  type,
  156.                                  state,
  157.                                  m_Buffer,
  158.                                  m_BufferSize,
  159.                                 &number_of_bytes_needed,
  160.                                 &number_of_services_returned,
  161.                                 &resume_handle ) == TRUE )
  162.       {
  163.          m_CurrentEntryNumber = 0;
  164.          m_NumberOfEntries    = number_of_services_returned;
  165.  
  166.          return( TRUE );
  167.       }
  168.       else
  169.       {
  170.          m_CurrentEntryNumber = 0;
  171.          m_NumberOfEntries    = 0;
  172.  
  173.          m_ErrorCode = ::GetLastError();
  174.  
  175.          return( FALSE );
  176.       }
  177.    }
  178.  
  179.    return( FALSE );
  180. }
  181.  
  182. DWORD CServiceControlManager::GetErrorCode( void ) const
  183. {
  184.    return( m_ErrorCode );
  185. }
  186.  
  187. BOOL CServiceControlManager::GetNext( CServiceNameAndStatus& status )
  188. {
  189.    if ( m_CurrentEntryNumber < m_NumberOfEntries )
  190.    {
  191.       status.Copy( &m_Buffer[ m_CurrentEntryNumber ] );
  192.       m_CurrentEntryNumber++;
  193.       return( TRUE );
  194.    }
  195.  
  196.    return( FALSE );
  197. }
  198.  
  199. BOOL CServiceControlManager::Install( LPCTSTR service_name, LPCTSTR friendly_name, LPCTSTR name_of_executable_file )
  200. {
  201.    ASSERT( service_name            != NULL );
  202.    ASSERT( friendly_name           != NULL );
  203.    ASSERT( name_of_executable_file != NULL );
  204.  
  205.    if ( service_name            == (LPCTSTR) NULL ||
  206.         friendly_name           == (LPCTSTR) NULL ||
  207.         name_of_executable_file == (LPCTSTR) NULL ||
  208.         m_ManagerHandle         == NULL )
  209.    {
  210.       m_ErrorCode == ERROR_INVALID_HANDLE;
  211.       return( FALSE );
  212.    }
  213.  
  214.    TRACE( "CServiceControlManager::Install()\n" );
  215.  
  216.    DWORD supported_types = EVENTLOG_ERROR_TYPE       | 
  217.                            EVENTLOG_WARNING_TYPE     |
  218.                            EVENTLOG_INFORMATION_TYPE |
  219.                            EVENTLOG_AUDIT_SUCCESS    |
  220.                            EVENTLOG_AUDIT_FAILURE;
  221.  
  222.    CEventLog event_log;
  223.  
  224.    TRACE( "CServiceControlManager::Install(), Creating Application log\n" );
  225.  
  226.    if ( event_log.CreateApplicationLog( service_name, name_of_executable_file, supported_types ) != TRUE )
  227.    {
  228.       return( FALSE );
  229.    }
  230.  
  231.    TRACE( "CServiceControlManager::Install(), Registering Source\n" );
  232.  
  233.    if ( event_log.RegisterSource( service_name ) != TRUE )
  234.    {
  235.       return( FALSE );
  236.    }
  237.  
  238.    SC_HANDLE service_handle = (SC_HANDLE) NULL;
  239.    
  240.    TRACE( "CServiceControlManager::Install(), Creating Service\n" );
  241.  
  242.    service_handle = ::CreateService( m_ManagerHandle,
  243.                                      service_name,
  244.                                      friendly_name,
  245.                                      SERVICE_ALL_ACCESS,
  246.                                      SERVICE_WIN32_OWN_PROCESS,
  247.                                      SERVICE_DEMAND_START,
  248.                                      SERVICE_ERROR_NORMAL,
  249.                                      name_of_executable_file,
  250.                                      NULL,
  251.                                      NULL,
  252.                                      "EventLog\0\0",
  253.                                      NULL,
  254.                                      NULL );
  255.  
  256.    TRACE( "CServiceControlManager::Install(), CreateService() returned\n" );
  257.  
  258.    if ( service_handle == (SC_HANDLE) NULL )
  259.    {
  260.       TRACE( "CServiceControlManager::Install(), service_handle == NULL\n" );
  261.  
  262.       m_ErrorCode = ::GetLastError();
  263.  
  264.       LPVOID message_buffer = (LPVOID) NULL;
  265.  
  266.       TRACE( "CServiceControlManager::Install(), Calling FormatMessage()\n" );
  267.  
  268.       ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  269.                        NULL,
  270.                        m_ErrorCode,
  271.                        MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
  272.              (LPTSTR) &message_buffer,
  273.                        0,
  274.                        NULL );
  275.  
  276.       TRACE( "CServiceControlManager::Install(), FormatMessage() returned\n" );
  277.  
  278.       if ( message_buffer != NULL )
  279.       {
  280.          TCHAR temp_string[ 255 ];
  281.  
  282.          TRACE( "CServiceControlManager::Install(), Calling wsprintf()\n" );
  283.          ::wsprintf( temp_string, "Can't create service because %s at line %d of CSvcMgr.cpp", message_buffer, __LINE__ );
  284.          TRACE( "CServiceControlManager::Install(), Calling ReportError()\n" );
  285.  
  286.          event_log.ReportError( temp_string );
  287.  
  288.       TRACE( "CServiceControlManager::Install(), Calling LocalFree()\n" );
  289.          ::LocalFree( message_buffer );
  290.       TRACE( "CServiceControlManager::Install(), LocalFree returned()\n" );
  291.       }
  292.  
  293.       TRACE( "CServiceControlManager::Install(), returning FALSE\n" );
  294.       return( FALSE );
  295.    }
  296.  
  297.    TRACE( "CServiceControlManager::Install(), Closeing service handle\n" );
  298.  
  299.    ::CloseServiceHandle( service_handle );
  300.    service_handle = (SC_HANDLE) NULL;
  301.  
  302.    /*
  303.    ** We successfully installed a new service, this is something we should log
  304.    */
  305.  
  306.    TCHAR user_name[ 2048 ];
  307.    TCHAR temp_string[ 2100 ];
  308.  
  309.    DWORD size_of_user_name = sizeof( user_name );
  310.  
  311.    ::ZeroMemory( user_name,   size_of_user_name     );
  312.    ::ZeroMemory( temp_string, sizeof( temp_string ) );
  313.  
  314.    TRACE( "CServiceControlManager::Install(), Getting User Name\n" );
  315.  
  316.    ::GetUserName( user_name, &size_of_user_name );
  317.  
  318.    TRACE( "CServiceControlManager::Install(), wsprintf()\n" );
  319.  
  320.    ::wsprintf( temp_string, "Service successfully installed by %s", user_name );
  321.  
  322.    TRACE( "CServiceControlManager::Install(), reporting\n" );
  323.  
  324.    event_log.ReportInformation( temp_string );
  325.  
  326.    TRACE( "CServiceControlManager::Install(), returning\n" );
  327.    return( TRUE );
  328. }
  329.  
  330. void CServiceControlManager::m_Initialize( void )
  331. {
  332.    m_ManagerHandle      = NULL;
  333.    m_ErrorCode          = 0;
  334.    m_Buffer             = NULL;
  335.    m_BufferSize         = 0;
  336.    m_CurrentEntryNumber = 0;
  337.    m_NumberOfEntries    = 0;
  338. }
  339.  
  340. BOOL CServiceControlManager::Open( DWORD what_to_open, LPCTSTR database_name, LPCTSTR machine_name )
  341. {
  342.    /*
  343.    ** database_name can be NULL
  344.    */
  345.  
  346.    if ( m_ManagerHandle != NULL )
  347.    {
  348.       Close();
  349.    }
  350.  
  351.    if ( machine_name == NULL )
  352.    {
  353.       m_MachineName.Empty(); // Should go and get our machine's name
  354.    }
  355.    else
  356.    {
  357.       m_MachineName = machine_name;
  358.    }
  359.  
  360.    m_ManagerHandle = ::OpenSCManager( machine_name, database_name, what_to_open );
  361.  
  362.    if ( m_ManagerHandle == NULL )
  363.    {
  364.       m_ErrorCode = ::GetLastError();
  365.       return( FALSE );
  366.    }
  367.    else
  368.    {
  369.       return( TRUE );
  370.    }
  371. }
  372.  
  373. BOOL CServiceControlManager::Pause( LPCTSTR service_name )
  374. {
  375.    ASSERT( service_name != NULL );
  376.  
  377.    if ( m_ManagerHandle == NULL )
  378.    {
  379.       m_ErrorCode = ERROR_INVALID_HANDLE;
  380.       return( FALSE );
  381.    }
  382.  
  383.    if ( service_name == NULL )
  384.    {
  385.       m_ErrorCode = ERROR_INVALID_HANDLE;
  386.       return( FALSE );
  387.    }
  388.  
  389.    SC_HANDLE service_handle = ::OpenService( m_ManagerHandle, service_name, SERVICE_PAUSE_CONTINUE );
  390.  
  391.    if ( service_handle == (SC_HANDLE) NULL )
  392.    {
  393.       m_ErrorCode = ::GetLastError();
  394.       return( FALSE );
  395.    }
  396.  
  397.    SERVICE_STATUS service_status;
  398.  
  399.    ::ZeroMemory( &service_status, sizeof( service_status ) );
  400.  
  401.    BOOL return_value = ::ControlService( service_handle, SERVICE_CONTROL_PAUSE, &service_status );
  402.  
  403.    if ( return_value != TRUE )
  404.    {
  405.       m_ErrorCode = ::GetLastError();
  406.    }
  407.  
  408.    ::CloseServiceHandle( service_handle );
  409.  
  410.    return( return_value );
  411. }
  412.  
  413. BOOL CServiceControlManager::Remove( LPCTSTR service_name )
  414. {
  415.    ASSERT( service_name != NULL );
  416.  
  417.    if ( service_name == (LPCTSTR) NULL || m_ManagerHandle == NULL )
  418.    {
  419.       m_ErrorCode = ERROR_INVALID_HANDLE;
  420.       return( FALSE );
  421.    }
  422.  
  423.    SC_HANDLE service_handle = (SC_HANDLE) NULL;
  424.  
  425.    service_handle = ::OpenService( m_ManagerHandle, service_name, SERVICE_ALL_ACCESS );
  426.  
  427.    if ( service_handle == (SC_HANDLE) NULL )
  428.    {
  429.       m_ErrorCode = ::GetLastError();
  430.       return( FALSE );
  431.    }
  432.  
  433.    /*
  434.    ** We're gonna delete the service, this is something we should record
  435.    */
  436.  
  437.    {
  438.       TCHAR user_name[ 2048 ];
  439.       TCHAR temp_string[ 2100 ];
  440.  
  441.       DWORD size_of_user_name = sizeof( user_name );
  442.  
  443.       ::ZeroMemory( user_name,   size_of_user_name     );
  444.       ::ZeroMemory( temp_string, sizeof( temp_string ) );
  445.  
  446.       ::GetUserName( user_name, &size_of_user_name );
  447.  
  448.       ::wsprintf( temp_string, "Service being removed by %s", user_name );
  449.  
  450.       CEventLog event_log( service_name );
  451.  
  452.       event_log.ReportInformation( temp_string );
  453.    }
  454.  
  455.    BOOL return_value = ::DeleteService( service_handle );
  456.  
  457.    if ( return_value != TRUE )
  458.    {
  459.       m_ErrorCode = ::GetLastError();
  460.  
  461.       /*
  462.       ** We couldn't delete the service, let's record why...
  463.       */
  464.  
  465.       LPVOID message_buffer = (LPVOID) NULL;
  466.  
  467.       ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  468.                        NULL,
  469.                        m_ErrorCode,
  470.                        MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ),
  471.              (LPTSTR) &message_buffer,
  472.                        0,
  473.                        NULL );
  474.  
  475.       if ( message_buffer != NULL )
  476.       {
  477.          TCHAR temp_string[ 255 ];
  478.  
  479.          ::wsprintf( temp_string, "Can't delete service because %s", message_buffer );
  480.  
  481.          CEventLog event_log( service_name );
  482.          event_log.ReportError( temp_string );
  483.  
  484.          ::LocalFree( message_buffer );
  485.       }
  486.  
  487.       return( FALSE );
  488.    }
  489.  
  490.    /*
  491.    ** Now that we've deleted the service, we need to remove it from the event logger
  492.    */
  493.  
  494.    CEventLog event_log;
  495.  
  496.    event_log.DeleteApplicationLog( service_name );
  497.  
  498.    return( TRUE );
  499. }
  500.  
  501. BOOL CServiceControlManager::Start( LPCTSTR service_name, DWORD service_argc, LPCTSTR *service_argv )
  502. {
  503.    ASSERT( service_name != NULL );
  504.  
  505.    if ( m_ManagerHandle == NULL )
  506.    {
  507.       m_ErrorCode = ERROR_INVALID_HANDLE;
  508.       return( FALSE );
  509.    }
  510.  
  511.    if ( service_name == NULL )
  512.    {
  513.       m_ErrorCode = ERROR_INVALID_HANDLE;
  514.       return( FALSE );
  515.    }
  516.  
  517.    SC_HANDLE service_handle = ::OpenService( m_ManagerHandle, service_name, SERVICE_START );
  518.  
  519.    if ( service_handle == (SC_HANDLE) NULL )
  520.    {
  521.       m_ErrorCode = ::GetLastError();
  522.       return( FALSE );
  523.    }
  524.  
  525.    BOOL return_value = ::StartService( service_handle, service_argc, service_argv );
  526.  
  527.    if ( return_value != TRUE )
  528.    {
  529.       m_ErrorCode = ::GetLastError();
  530.    }
  531.  
  532.    ::CloseServiceHandle( service_handle );
  533.  
  534.    return( return_value );
  535. }
  536.  
  537. BOOL CServiceControlManager::Stop( LPCTSTR service_name )
  538. {
  539.    ASSERT( service_name != NULL );
  540.  
  541.    if ( m_ManagerHandle == NULL )
  542.    {
  543.       m_ErrorCode = ERROR_INVALID_HANDLE;
  544.       return( FALSE );
  545.    }
  546.  
  547.    if ( service_name == NULL )
  548.    {
  549.       m_ErrorCode = ERROR_INVALID_HANDLE;
  550.       return( FALSE );
  551.    }
  552.  
  553.    SC_HANDLE service_handle = ::OpenService( m_ManagerHandle, service_name, SERVICE_STOP );
  554.  
  555.    if ( service_handle == (SC_HANDLE) NULL )
  556.    {
  557.       m_ErrorCode = ::GetLastError();
  558.       return( FALSE );
  559.    }
  560.  
  561.    SERVICE_STATUS service_status;
  562.  
  563.    ::ZeroMemory( &service_status, sizeof( service_status ) );
  564.  
  565.    BOOL return_value = ::ControlService( service_handle, SERVICE_CONTROL_STOP, &service_status );
  566.  
  567.    if ( return_value != TRUE )
  568.    {
  569.       m_ErrorCode = ::GetLastError();
  570.    }
  571.  
  572.    ::CloseServiceHandle( service_handle );
  573.  
  574.    return( return_value );
  575. }
  576.  
  577. CServiceNameAndStatusA::CServiceNameAndStatusA()
  578. {
  579.    Empty();
  580. }
  581.  
  582. CServiceNameAndStatusA::~CServiceNameAndStatusA()
  583. {
  584.    Empty();
  585. }
  586.  
  587. void CServiceNameAndStatusA::Copy( const _ENUM_SERVICE_STATUSA *source_p )
  588. {
  589.    ASSERT( source_p != NULL );
  590.  
  591.    if ( source_p == NULL )
  592.    {
  593.       Empty();
  594.       return;
  595.    }
  596.  
  597.    lpServiceName                           = source_p->lpServiceName;
  598.    lpDisplayName                           = source_p->lpDisplayName;
  599.    ServiceStatus.dwServiceType             = source_p->ServiceStatus.dwServiceType;
  600.    ServiceStatus.dwCurrentState            = source_p->ServiceStatus.dwCurrentState;
  601.    ServiceStatus.dwControlsAccepted        = source_p->ServiceStatus.dwControlsAccepted;
  602.    ServiceStatus.dwWin32ExitCode           = source_p->ServiceStatus.dwWin32ExitCode;
  603.    ServiceStatus.dwServiceSpecificExitCode = source_p->ServiceStatus.dwServiceSpecificExitCode;
  604.    ServiceStatus.dwCheckPoint              = source_p->ServiceStatus.dwCheckPoint;
  605.    ServiceStatus.dwWaitHint                = source_p->ServiceStatus.dwWaitHint;
  606. }
  607.  
  608. void CServiceNameAndStatusA::Empty( void )
  609. {
  610.    lpServiceName                           = NULL;
  611.    lpDisplayName                           = NULL;
  612.    ServiceStatus.dwServiceType             = 0;
  613.    ServiceStatus.dwCurrentState            = 0;
  614.    ServiceStatus.dwControlsAccepted        = 0;
  615.    ServiceStatus.dwWin32ExitCode           = 0;
  616.    ServiceStatus.dwServiceSpecificExitCode = 0;
  617.    ServiceStatus.dwCheckPoint              = 0;
  618.    ServiceStatus.dwWaitHint                = 0;
  619. }
  620.  
  621. CServiceNameAndStatusW::CServiceNameAndStatusW()
  622. {
  623.    Empty();
  624. }
  625.  
  626. CServiceNameAndStatusW::~CServiceNameAndStatusW()
  627. {
  628.    Empty();
  629. }
  630.  
  631. void CServiceNameAndStatusW::Copy( const _ENUM_SERVICE_STATUSW *source_p )
  632. {
  633.    ASSERT( source_p != NULL );
  634.  
  635.    if ( source_p == NULL )
  636.    {
  637.       Empty();
  638.       return;
  639.    }
  640.  
  641.    lpServiceName                           = source_p->lpServiceName;
  642.    lpDisplayName                           = source_p->lpDisplayName;
  643.    ServiceStatus.dwServiceType             = source_p->ServiceStatus.dwServiceType;
  644.    ServiceStatus.dwCurrentState            = source_p->ServiceStatus.dwCurrentState;
  645.    ServiceStatus.dwControlsAccepted        = source_p->ServiceStatus.dwControlsAccepted;
  646.    ServiceStatus.dwWin32ExitCode           = source_p->ServiceStatus.dwWin32ExitCode;
  647.    ServiceStatus.dwServiceSpecificExitCode = source_p->ServiceStatus.dwServiceSpecificExitCode;
  648.    ServiceStatus.dwCheckPoint              = source_p->ServiceStatus.dwCheckPoint;
  649.    ServiceStatus.dwWaitHint                = source_p->ServiceStatus.dwWaitHint;
  650. }
  651.  
  652. void CServiceNameAndStatusW::Empty( void )
  653. {
  654.    lpServiceName                           = NULL;
  655.    lpDisplayName                           = NULL;
  656.    ServiceStatus.dwServiceType             = 0;
  657.    ServiceStatus.dwCurrentState            = 0;
  658.    ServiceStatus.dwControlsAccepted        = 0;
  659.    ServiceStatus.dwWin32ExitCode           = 0;
  660.    ServiceStatus.dwServiceSpecificExitCode = 0;
  661.    ServiceStatus.dwCheckPoint              = 0;
  662.    ServiceStatus.dwWaitHint                = 0;
  663. }
  664.